/* eslint-disable */
import Layers from "../layers";
import ArrayUtils from "../commons/array-utils";
import TreeNode from "./tree-node";
import TreeBuilder, {mapper_default, TreeMapper} from "./tree-builder";
import {DictionaryItem} from "../types";


/**
 * 默认的数据映射函数
 */
const mapper_dictionary: TreeMapper = (ele: unknown, ret: TreeNode): void => {
    ret.data = ele;

    ret.id = ele.value;
    ret.pid = '';

    ret.label = ele.label;
}

/**
 * 默认的格式化函数
 * @param obj  - 任意输入值
 * @param node   - 树节点
 */
export const mapper_default: TreeMapper = (obj: any, node: TreeNode): void => {
    node.data = obj;

    node.id = obj.id;
    node.pid = obj.pid;

    node.icon = obj.icon;
    node.label = obj.label;
    node.checked = obj.checked;
}


/**
 * 构建 TreeBuilder 对象
 *
 * @return {TreeBuilder}
 */
const builder = (): TreeBuilder => {
    return new TreeBuilder();
}


/**
 * 格式化数据，转换成 TreeNode
 *
 * @param obj 单独一个元素
 * @param mapper 格式化参数名设置
 * @return 树节点
 */
const wrapObject = (obj: any, mapper: TreeMapper): TreeNode => {
    const node = new TreeNode();
    mapper(obj, node);
    return node;
};

/**
 * 格式化数据，转换成 TreeNode
 *
 * 如果存在子节点，会递归检索子节点
 *
 * @param list 数据列表
 * @param mapper 格式化参数名设置
 * @return 树节点列表
 */
const wrapList = (list: any[], mapper: TreeMapper): TreeNode[] => {
    return ArrayUtils.wrapArray(list, e => wrapObject(e, mapper));
}


/**
 * 格式化数据，转换成 TreeNode 数组
 *
 * @param list 数据列表
 * @param mapper 格式化参数名设置
 * @return 树
 */
const toTree = (list: any[], mapper: TreeMapper = mapper_default): TreeNode[] => {
    if (Layers.isEmpty(list)) {
        return [];
    } else {
        return new TreeBuilder().setMapper(mapper).setParents(list).groupBy().getRoot();
    }
}

/**
 * 数据字典与数组合并，形成树形结构
 *
 * @param options - 数据字典
 * @param list - 数据列表
 * @param mapper - 数据映射函数
 * @return 树
 */
const mergeDictionaryList = (options: DictionaryItem[], list: any[], mapper: TreeMapper = mapper_default): TreeNode[] => {
    if (options == null) {
        return [];
    } else {
        return new TreeBuilder()
            .setMapper(mapper_dictionary).setParents(options)
            .setMapper(mapper).setChildren(list)
            .groupBy().getParent();
    }
}

/**
 * 获取树所有节点的某个字段，将结果加入到数组中。
 *
 * @param list 树
 * @param field 字段名
 * @returns 字段值数组
 */
const getTreeFields = (list: TreeNode[], field: string): string[] => {
    let res = [];
    if (Layers.isNotEmpty(list)) {
        forEach(list, ele => res.push(ele[field]));
    }
    return res;
}

/**
 * 获取 id 串
 *
 * @param list 树
 * @returns 字段值数组
 */
const getIds = (list: TreeNode[]) => {
    return getTreeFields(list, 'id');
};

/**
 * 获取 name 串
 *
 * @param list 树
 * @returns 字段值数组
 */
const getNames = (list: TreeNode[]) => {
    return getTreeFields(list, 'name');
};

/**
 * 获取 label 串
 *
 * @param list 树
 * @returns 字段值数组
 */
const getLabels = (list: TreeNode[]) => {
    return getTreeFields(list, 'label');
};

/**
 * 遍历所有的节点
 *
 * 注意：遍历无法提前终止
 *
 * @param tree 树
 * @param call 回调函数
 */
const forEach = (tree: TreeNode[], call: (node: TreeNode) => void) => {
    if (Layers.isNotEmpty(tree)) {
        let node;
        for (let i = 0; i < tree.length; i++) {
            node = tree[i];
            call(node)
            if (node.hasChildren()) {
                forEach(node.children, call)
            }
        }
    }
};

/**
 * 取出满足条件的第一个元素
 *
 * @param tree 树
 * @param filter 返回 true 的时候，取出该元素
 * @returns 符合条件的第一个元素
 */
const findElement = (tree: TreeNode[], filter: (node: TreeNode) => boolean): TreeNode => {
    if (Layers.isNotEmpty(tree)) {
        let node;
        for (let i = 0; i < tree.length; i++) {
            node = tree[i];
            if (filter(node)) {
                return node;
            }
            if (node.hasChildren()) {
                let ret = findElement(node.children, filter);
                if (ret) {
                    return ret;
                }
            }
        }
    }
};

/**
 * 取出满足条件的所有元素，并将所有节点加入到数组中
 *
 * @param tree 树
 * @param filter 返回 true 的时候，取出该元素
 * @returns 符合条件的节点数组
 */
const findElements = (tree: TreeNode[], filter: (node: TreeNode) => boolean): TreeNode[] => {
    if (Layers.isNotEmpty(tree)) {
        let ret = [];
        forEach(tree, e => {
            if (filter(e)) {
                ret.push(e);
            }
        });
        return ret;
    } else {
        return tree;
    }
};


/**
 * 根据 id 查找特定的节点
 *
 * @param tree 树
 * @param id 节点 ID 值
 * @returns 符合条件的第一个节点
 */
const getNodeById = (tree: TreeNode[], id: string | number): TreeNode => {
    return findElement(tree, e => e.id === id);
};

/**
 * 根据 id 查找特定的节点
 *
 * @param tree 树
 * @param ids 节点 ID 数组
 * @returns ids数组对应的节点数组
 */
const getNodeByIds = (tree: TreeNode[], ids: string[] | number[]): TreeNode[] => {
    return findElements(tree, e => Layers.contain(ids, e.id));
};


/**
 * 取出满足条件的所有元素
 *
 * @param tree {[TreeNode]}         树
 * @param label {string}            模糊检索字符
 * @return {[TreeNode]}             至少是一个空的数组
 */
const searchElements = (tree, label) => {
    if (Layers.isNotEmpty(tree)) {
        // 重置所有状态
        forEach(tree, ele => ele.hit = false);

        // 递归查找
        forEach(tree, e => {
            if (!e.hit && e.label.includes(label)) {
                e.hit = true;
                let node = e;
                while (node.hasParent()) {
                    node = node.parent;
                    if (node.hit) {
                        break;
                    } else {
                        node.hit = true;
                    }
                }
            }
        });

        // 只遍历第一层数据
        return Layers.findElements(tree, ele => ele.isHit());
    } else {
        return tree;
    }
};

/**
 * 取出满足条件的所有元素
 *
 * 扁平化搜索结果，将查到的元素放到一个数组中去。
 *
 * 使用场景：
 *
 * @param tree {[TreeNode]}         树
 * @param label {string}            模糊检索字符
 * @return {[TreeNode]}             至少是一个空的数组
 */
const smartSearchElements = (tree, label) => {
    if (Layers.isNotEmpty(tree)) {
        const ret = [];

        // 重置所有状态
        forEach(tree, e => {
            if (e.label.includes(label)) {
                ret.push(e);
            }
        });

        return ret;
    } else {
        return tree;
    }
};

/**
 * 判断是不是一棵树
 *
 * 当前函数的设计目的，是为了避免重复解析数据，
 * 代码并不十分严谨，不过无需调整，当前环境下满足使用需求，
 *
 * @param list {[]}       集合
 * @return {boolean}
 */
const isTree = (list: []) => {
    if (Layers.isNotEmpty(list)) {
        return list[0] instanceof TreeNode;
    } else {
        return true;
    }
}

/**
 * 树形工具
 */
const Bamboo = {
    builder, wrapObject, wrapList,

    toTree, mergeDictionaryList,

    getTreeFields, getIds, getLabels, getNames,

    forEach, findElement, findElements,

    searchElements, smartSearchElements,

    getNodeByIds, getNodeById,

    isTree
};

export {TreeMapper, TreeNode};

export default Bamboo;
